package web

import (
	"embed"
	"errors"
	"fmt"
	"go-quick/interface/ilogger"
	"go-quick/interface/iweb"
	"go-quick/logger"
	"go-quick/web/interceptor"
	"net/http"
	"strings"
	"sync"
)

type (
	// QuickWebEngine web引擎
	QuickWebEngine struct {
		log               ilogger.ILogger
		Port              int64                     // 端口
		tree              map[string]iweb.IRouter   // 路由tree
		pool              sync.Pool                 // 上下文对象池
		interceptors      []iweb.IRouterInterceptor // 拦截器
		basePath          string                    // 基础路径
		notfoundPath      iweb.HandlerFunc          // 路由没有找到
		beforeInterceptor iweb.HandlerFunc          // 前置拦截器拦截处理器
		afterInterceptor  iweb.HandlerFunc          // 后置拦截器拦截处理器
	}
)

func (engine *QuickWebEngine) port() string {
	return fmt.Sprintf(":%d", engine.Port)
}

// ServeHTTP Handler
func (engine *QuickWebEngine) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	// 初始化上下文
	ctx := engine.getPool()
	ctx.Build(response, request)
	// 匹配路径对应的handler
	_ = engine.matchPath(ctx)
	engine.putPool(ctx)
}

func (engine *QuickWebEngine) getPool() iweb.IHttpContext {
	ctx := engine.pool.Get()
	if ctx == nil {
		ctx = NewContext(engine.log)
	}
	return ctx.(iweb.IHttpContext)
}

// before 执行拦截器前置方法
func (engine *QuickWebEngine) before(ctx iweb.IHttpContext) error {
	// 执行拦截器前置方法
	if len(engine.interceptors) > 0 {
		for _, routerInterceptor := range engine.interceptors {
			if !routerInterceptor.Before(ctx) {
				engine.log.WarnF("请求[%s:%s] => 前置[%s]验证失败", ctx.Method(), ctx.Path(), routerInterceptor.Name())
				return errors.New(routerInterceptor.Name() + "前置Handler验证失败")
			}
		}
	}
	return nil
}

// after 执行拦截器后置方法
func (engine *QuickWebEngine) after(ctx iweb.IHttpContext) error {
	// 执行拦截器后置方法
	if len(engine.interceptors) > 0 {
		for _, routerInterceptor := range engine.interceptors {
			if !routerInterceptor.After(ctx) {
				engine.log.WarnF("请求[%s:%s] => 后置[%s]验证失败", ctx.Method(), ctx.Path(), routerInterceptor.Name())
				return errors.New(routerInterceptor.Name() + "后置Handler验证失败")
			}
		}
	}
	return nil
}

func (engine *QuickWebEngine) matchPath(ctx iweb.IHttpContext) error {

	var executeHandler = func() error {
		defer func() {
			if err := recover(); err != nil {
				engine.log.ErrorF("请求[%s:%s] => 执行发生异常：%s", ctx.Method(), ctx.Path(), err)
				ctx.Response().Json(500, err)
			}
		}()
		engine.Match(ctx)(ctx)
		return nil
	}

	// 执行拦截器前置方法
	if err := engine.before(ctx); err != nil {
		engine.beforeInterceptor(ctx)
		return nil
	}
	_ = executeHandler()
	// 执行拦截器后置方法
	if err := engine.after(ctx); err != nil {
		engine.afterInterceptor(ctx)
		return nil
	}
	return nil
}

func (engine *QuickWebEngine) Match(ctx iweb.IHttpContext) iweb.HandlerFunc {
	var (
		path             = ctx.Path()
		method           = ctx.Method()
		params           = new(HttpRouterParams)
		pathArray        []string
		findRouter       func(int, []string, *HttpRouterParams, iweb.IRouter) (iweb.IRouter, error)
		findStaticRouter func(path string, router iweb.IRouter) (iweb.IRouter, error)

		router  iweb.IRouter
		handler iweb.HandlerFunc
		exist   bool
		err     error
	)
	// 递归查找路由节点
	findRouter = func(i int, paths []string, params *HttpRouterParams, router iweb.IRouter) (iweb.IRouter, error) {
		if router.Path() == paths[i] || strings.HasPrefix(router.Path(), ":") {
			if strings.HasPrefix(router.Path(), ":") {
				params.params = append(params.params, HttpRouterParam{key: router.Path()[1:], value: paths[i]})
			}
			if i+1 >= len(paths) {
				return router, nil
			}
			for _, child := range router.Children() {
				if foundRouter, err := findRouter(i+1, paths, params, child); err == nil {
					return foundRouter, nil
				}
			}
		}
		return nil, errors.New("not found path")
	}
	findStaticRouter = func(path string, router iweb.IRouter) (iweb.IRouter, error) {
		return router, nil
	}

	pathArray = strings.Split(path[1:], "/")
	if router, err = findRouter(0, pathArray, params, engine.tree["api"]); err != nil {
		if router, err = findStaticRouter(path, engine.tree["static"]); err != nil {
			engine.log.WarnF("没有匹配的请求路由 [%s:%s]", method, path)
			return engine.notfoundPath
		}
	}
	// 是否存在对应的Handler
	if handler, exist = router.Handler(method); !exist {
		engine.log.WarnF("路由请求方式不支持 [%s:%s]", method, path)
		return engine.notfoundPath
	}
	ctx.Request().SetParam(params)
	return handler
}

func (engine *QuickWebEngine) putPool(ctx iweb.IHttpContext) {
	engine.pool.Put(ctx)
}

// Start 启动
func (engine *QuickWebEngine) Start() {
	if err := http.ListenAndServe(engine.port(), engine); err != nil {
		engine.log.FailF("启动服务器失败:%s", err.Error())
		panic(err)
	}
	engine.log.Info("启动HTTP服务器成功")
}

// Routes 构建路由表
func (engine *QuickWebEngine) Routes(basePath ...string) iweb.IRouter {
	if len(basePath) > 0 {
		engine.basePath = basePath[0]
	} else {
		engine.basePath = ""
	}
	router := NewRouter(engine.basePath)
	engine.tree["api"] = router
	return router
}

func (engine *QuickWebEngine) StaticEmbed(urlPath string, filepath string, embed embed.FS) {
	engine.tree["static"] = NewStaticRouter(urlPath, filepath, embed)
}

// Default 默认
func Default() *QuickWebEngine {
	return &QuickWebEngine{
		log:          logger.Default(),
		Port:         8080,
		tree:         make(map[string]iweb.IRouter),
		interceptors: []iweb.IRouterInterceptor{interceptor.NewLogger()},
		notfoundPath: func(ctx iweb.IHttpContext) {
			ctx.Log().ErrorF("资源[%s:%s]没有找到", ctx.Method(), ctx.Path())
		},
	}
}
